package com.shiro.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.shiro.entity.SysPermission;
import com.shiro.entity.SysRole;
import com.shiro.entity.UserInfo;
import com.shiro.sevice.UserInfoService;

import javax.annotation.Resource;

public class MyShiroRealm extends AuthorizingRealm {

	@Resource
	private UserInfoService userInfoService;

	/**
	 * 在认证、授权内部实现机制中都有提到，最终处理都将交给Real进行处理。因为在Shiro中，
	 * 最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下， 在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说，
	 * Realm是专用于安全框架的DAO. Shiro的认证过程最终会交由Realm执行， 这时会调用Realm的
	 * getAuthenticationInfo(token) 方法。
	 * 
	 * 该方法主要执行以下操作: •1、检查提交的进行认证的令牌信息 •2、根据令牌信息从数据源(通常为数据库)中获取用户信息 •3、对用户信息进行匹配验证。
	 * •4、验证通过将返回一个封装了用户信息的 AuthenticationInfo 实例。 •5、验证失败则抛出
	 * AuthenticationException 异常信息。
	 * 
	 * 而在我们的应用程序中要做的就是自定义一个Realm类，继承AuthorizingRealm抽象类，重载doGetAuthenticationInfo()，重写获取用户信息的方法。
	 * 
	 * doGetAuthenticationInfo的重写
	 * 
	 */
	/* 主要是用来进行身份认证的，也就是说验证用户输入的账号和密码是否正确。 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
		// 获取用户的输入的账号.
		String username = (String) token.getPrincipal();
		System.out.println(token.getCredentials());
		// 通过username从数据库中查找 User对象，如果找到，没找到.
		// 实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
		UserInfo userInfo = userInfoService.findByUsername(username);
		System.out.println("----->>userInfo=" + userInfo);
		if (userInfo == null) {
			return null;
		}
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, // 用户名
				userInfo.getPassword(), // 密码
				ByteSource.Util.bytes(userInfo.getCredentialsSalt()), // salt=username+salt
				getName() // realm name
		);
		return authenticationInfo;
	}

	/**
	 * shiro的权限授权是通过继承 AuthorizingRealm 抽象类，重载 doGetAuthorizationInfo();
	 * 当访问到页面的时候，链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行，
	 * 所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现， 直接返回null即可。在这个方法中主要是使用类：
	 * SimpleAuthorizationInfo 进行角色的添加和权限的添加。
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		// 获取登录的用户信息
		UserInfo userInfo = userInfoService.findByUsername(((UserInfo) principals.getPrimaryPrincipal()).getUsername());
		for (SysRole role : userInfo.getRoleList()) {
			authorizationInfo.addRole(role.getRole());
			for (SysPermission p : role.getPermissions()) {
				authorizationInfo.addStringPermission(p.getPermission());
			}
		}
		return authorizationInfo;
	}

}